﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace Jermaine.Cache
{
    /// <summary>
    /// CacheServices
    /// </summary>
    class CacheService : ICacheService, IDisposable
    {

        #region Internal Class
        /// <summary>
        /// 存储TimedObject容器,提供增删改查
        /// </summary>
        class StoredObjects
        {
            private IDictionary<string, TimedObject> objects;
            private CacheService service;
            private object syncRoot;

            public StoredObjects(CacheService service) {
                this.service = service;
                this.objects = new Dictionary<string, TimedObject>();
                this.syncRoot = ((ICollection)objects).SyncRoot;
            }

            /// <summary>
            /// Add item
            /// </summary>
            /// <param name="id"></param>
            /// <param name="o"></param>
            /// <returns></returns>
            public bool Add(string id, TimedObject o) {
                lock (syncRoot) {
                    if (objects.ContainsKey(id)) {
                        return false;
                    }
                    objects.Add(id, o);
                }

                this.service.OnItemAdded(new CacheItemEventArgs {
                    Id = id,
                    Time = System.DateTime.Now,
                    NewItem = o,
                    ItemCount = this.objects.Count

                });
                return true;
            }

            public int Count {
                get {
                    var count = 0;
                    lock (syncRoot) {
                        count = objects.Count;
                    }
                    return count;
                }
            }

            /// <summary>
            /// Update Item
            /// </summary>
            /// <param name="id"></param>
            /// <param name="o"></param>
            /// <returns></returns>
            public bool Update(string id, TimedObject o) {
                TimedObject oldValue = null;
                lock (syncRoot) {
                    if (!objects.ContainsKey(id)) {
                        return false;
                    }
                    oldValue = objects[id];
                    objects[id] = o;

                }
                this.service.OnItemUpdate(new CacheItemEventArgs {
                    Id = id,
                    Time = System.DateTime.Now,
                    NewItem = o,
                    OldItem = oldValue,
                    ItemCount = this.objects.Count
                });
                return true;
            }

            /// <summary>
            /// GetItem
            /// </summary>
            /// <param name="id"></param>
            /// <returns></returns>
            public object Get(string id) {
                TimedObject o = null;
                lock (syncRoot) {
                    if (objects.ContainsKey(id)) {
                        o = objects[id];
                    }
                }
                if (o != null) {
                    return o.Mirror;
                }
                return null;
            }

            /// <summary>
            /// Remove Item
            /// </summary>
            /// <param name="id"></param>
            /// <returns></returns>
            public bool Remove(string id) {
                TimedObject o = null;
                lock (syncRoot) {
                    if (!objects.ContainsKey(id)) {
                        return false;
                    }
                    o = objects[id];
                    objects.Remove(id);

                }
                this.service.OnItemRemoved(new CacheItemEventArgs {
                    Id = id,
                    Time = System.DateTime.Now,
                    OldItem = o,
                    ItemCount = this.objects.Count
                });
                return true;
            }

            /// <summary>
            /// 
            /// </summary>
            /// <param name="Ids"></param>
            /// <returns></returns>
            public bool Remove(IEnumerable<string> Ids) {

                foreach (var id in Ids) {
                    var success = Remove(id);
                    if (!success) {
                        return false;
                    }
                }
                return true;
            }

            /// <summary>
            /// 是否存在
            /// </summary>
            /// <param name="id"></param>
            /// <returns></returns>
            public bool Exists(string id) {
                var exists = false;
                lock (syncRoot) {
                    exists = this.objects.ContainsKey(id);
                }
                return exists;
            }
            /// <summary>
            /// 清空
            /// </summary>
            /// <returns></returns>
            public bool Clear() {
                lock (syncRoot) {
                    objects.Clear();
                }
                return true;
            }

            /// <summary>
            /// 获取所有的对象
            /// </summary>
            public IEnumerable<TimedObject> TimedObjects {
                get {
                    var list = new List<TimedObject>();
                    lock (syncRoot) {
                        foreach (var item in objects.Values.Where(o => o.ExpiredTime != null)) {
                            list.Add(item);
                        }
                    }
                    var cloneList = new List<TimedObject>();
                    foreach (var item in list) {
                        TimedObject o = item.Mirror;
                        if (o != null) {
                            cloneList.Add(o);
                        }

                    }
                    return cloneList;
                }
            }
        }
        #endregion

        #region 字段
        private StoredObjects store;

        private System.Timers.Timer timer;

        public event EventHandler<CacheItemEventArgs> ItemRemoved;

        public event EventHandler<CacheItemEventArgs> ItemAdded;

        public event EventHandler<CacheItemEventArgs> ItemUpdated;

        public event EventHandler Created;
        #endregion

        /// <summary>
        /// 创建一个新的Services
        /// </summary>
        /// <returns></returns>
        public static CacheService Create() {
            return new CacheService();
        }

        #region Fire events

        protected void OnItemRemoved(CacheItemEventArgs e) {
            if (this.ItemRemoved != null) {
                e.ItemCount = store.Count;
                ItemRemoved(this, e);
            }
        }

        protected void OnItemUpdate(CacheItemEventArgs e) {
            if (this.ItemUpdated != null) {
                e.ItemCount = store.Count;
                ItemUpdated(this, e);
            }
        }

        protected void OnItemAdded(CacheItemEventArgs e) {
            if (this.ItemAdded != null) {
                e.ItemCount = store.Count;
                ItemAdded(this, e);
            }
        }

        protected void OnCreated() {
            if (this.Created != null) {
                Created(this, EventArgs.Empty);
            }
        }

        #endregion

        private CacheService() {
            store = new StoredObjects(this);
            ClearExpired();
            OnCreated();
        }

        #region Clear Expired objects
        private void ClearExpired() {
            timer = new System.Timers.Timer();
            timer.Elapsed += TimerElapsed;
            timer.Interval = 1;
            timer.Enabled = true;
            timer.Start();
        }

        private void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e) {
            var expires = store.TimedObjects
                         .Where(o => o.ExpiredTime.IsExpired).Select(o => o.Id);
            store.Remove(expires);
        }
        #endregion

        /// <summary>
        /// 添加项
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id"></param>
        /// <param name="o"></param>
        /// <param name="expireTime"></param>
        /// <returns></returns>
        public bool Put<T>(string id, T o, ExpiredTime expireTime = null) {
            try {
                var existed = this.store.Exists(id);
                if (existed) {
                    return this.store.Update(id, new TimedObject {
                        ExpiredTime = expireTime,
                        Id = id,
                        Item = o
                    });
                } else {
                    return this.store.Add(id, new TimedObject {
                        ExpiredTime = expireTime,
                        Id = id,
                        Item = o
                    });
                }
            } catch {
                return false;
            }
        }

        /// <summary>
        /// 是否存在
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool Exists(string id) {
            return store.Exists(id);
        }

        /// <summary>
        /// 获取项
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id"></param>
        /// <returns></returns>
        public T Get<T>(string id) {
            var o = store.Get(id);
            if (o != null) {
                try {
                    return (T)o;
                } catch {
                    return default(T);
                }
            }
            return default(T);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public bool Remove(string id) {
            try {
                return this.store.Remove(id);
            } catch {
                return false;
            }
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="expireTime"></param>
        /// <returns></returns>
        public bool Remove(ExpiredTime expireTime) {
            try {
                var expires = store.TimedObjects
                        .Where(o => o.ExpiredTime.ExpiredAt(expireTime))
                        .Select(o => o.Id);
                return this.store.Remove(expires);

            } catch {
                return false;
            }
        }

        /// <summary>
        /// 清空
        /// </summary>
        /// <returns></returns>
        public bool Clear() {
            try {
                return store.Clear();
            } catch {
                return false;
            }
        }

        #region IDisposable Members

        public void Dispose() {
            if (timer != null) {
                timer.Enabled = false;
                timer.Stop();
                timer.Elapsed -= TimerElapsed;
                timer.Dispose();
            }
        }

        #endregion
    }
}
